#include <string.h>

#include "..\UnitTestFramework\UnitTest.h"
#include "..\StorageEngine\Page.h"
#include "..\StorageEngine\Heap.h"
#include "..\StorageEngine\BufferManager.h"

using namespace Hush::Database;
using namespace Hush;

using namespace Hush::UnitTest;

// There is no buffer overrun check
// The string size should be correctly aligned on 32bit.
template <Int32 Size>
struct EmployeeRecord
{
    EmployeeRecord(Int32 employeeId, Int32 departmentId, const Char* name)
        : EmployeeId(employeeId), DepartmentId(departmentId)
    {
        // No buffer check
        wcscpy_s(Name, name);
    }
    Int32 EmployeeId;
    Int32 DepartmentId;
    Char Name[(Size-8)/sizeof(Char)];
};

typedef EmployeeRecord<8*sizeof(Char)+8> SmallRecord1;
typedef EmployeeRecord<16*sizeof(Char)+8> SmallRecord2;
typedef EmployeeRecord<DataPageData::DataRegionSize-1280> LargeRecord;


template<typename THeap>
class HeapTestsBase : public TestClassBase<HeapTestsBase<THeap> >
{
public:
    TESTMETHOD(SimpleTest)
    {        
        BufferManager bufferManager;        

        THeap heap(&bufferManager);

        SmallRecord1 e1(1, 1, L"Justin");
        SmallRecord2 e2(1, 1, L"Kane");        

        RowId row1 = heap.InsertRow(MakeRowPtr(&e1));
        RowId row2 = heap.InsertRow(MakeRowPtr(&e2));

        DataPage* page = bufferManager.GetPageAs<DataPage>(row1.pageId);
        RowPtr ptr1 = page->GetRowPtr(row1);

        Assert::AreEqual(e1.EmployeeId, ((SmallRecord1*)ptr1.data)->EmployeeId, L"The employee id is not correct for row 1.");
        Assert::AreEqual(e1.DepartmentId, ((SmallRecord1*)ptr1.data)->DepartmentId, L"The department id is not correct for row 1.");

        RowPtr ptr2 = page->GetRowPtr(row2);
        
        Assert::AreEqual(e2.EmployeeId, ((SmallRecord2*)ptr2.data)->EmployeeId, L"The employee id is not correct for row 2.");
        Assert::AreEqual(e2.DepartmentId, ((SmallRecord2*)ptr2.data)->DepartmentId, L"The department id is not correct for row 2.");
    }

    
    TESTMETHOD(TestSimpleScan)
    {
        BufferManager bufferManager;
        Heap heap(&bufferManager);

        SmallRecord1 e1(1, 1, L"Justin");
        SmallRecord2 e2(2, 1, L"Kane");        

        heap.InsertRow(MakeRowPtr(&e1));
        heap.InsertRow(MakeRowPtr(&e2));

        IScan* scan = heap.OpenScan();

        RowPtr ptr1 = scan->GetNextRow();
        RowPtr ptr2 = scan->GetNextRow();
        RowPtr ptr3 = scan->GetNextRow();

        delete scan;

        Assert::AreEqual(RowPtr::EndOfRow, ptr3.length, L"The row count returned is not correct.");

        Assert::AreEqual(e1.EmployeeId, ((SmallRecord1*)ptr1.data)->EmployeeId, L"The employee id is not correct for row 1.");
        Assert::AreEqual(e1.DepartmentId, ((SmallRecord1*)ptr1.data)->DepartmentId, L"The department id is not correct for row 1.");
        
        Assert::AreEqual(e2.EmployeeId, ((SmallRecord2*)ptr2.data)->EmployeeId, L"The employee id is not correct for row 2.");
        Assert::AreEqual(e2.DepartmentId, ((SmallRecord2*)ptr2.data)->DepartmentId, L"The department id is not correct for row 2.");
    }

};

TESTCLASS(SimpleHeapTests)
{
public:
    virtual void RunTests()
    {
        HeapTestsBase<SimpleHeap> tests;
        tests.RunTests();
        TestClassBase::RunTests();
    }

    TESTMETHOD(TestNextPageSetCorrectly)
    {
        BufferManager bufferManager;
        SimpleHeap heap(&bufferManager);        
        
        LargeRecord e1(1, 1, L"Justin");
        LargeRecord e2(2, 1, L"Kane");  

        RowId row1 = heap.InsertRow(MakeRowPtr(&e1));
        RowId row2 = heap.InsertRow(MakeRowPtr(&e2));

        DataPage* headerPage = bufferManager.GetPageAs<DataPage>(heap.GetHeaderPageId());
        Assert::AreEqual(row2.pageId, headerPage->GetNextPageId(), L"Next page id is not set correctly.");

    }
};



TESTCLASS(HeapTests)
{
public:
    virtual void RunTests()
    {
        HeapTestsBase<Heap> tests;
        tests.RunTests();
        TestClassBase::RunTests();
    }

    TESTMETHOD(TestInsertTwoLargeRows)
    {
        BufferManager bufferManager;
        Heap heap(&bufferManager);        
        
        LargeRecord e1(1, 1, L"Justin");
        LargeRecord e2(2, 1, L"Kane");  

        heap.InsertRow(MakeRowPtr(&e1));
        heap.InsertRow(MakeRowPtr(&e2));

        IScan* scan = heap.OpenScan();

        RowPtr ptr1 = scan->GetNextRow();
        RowPtr ptr2 = scan->GetNextRow();
        RowPtr ptr3 = scan->GetNextRow();

        delete scan;        

        Assert::AreNotEqual(RowPtr::EndOfRow, ptr1.length, L"row1 is EndOfRow.");
        Assert::AreEqual(e1.EmployeeId, ((LargeRecord*)ptr1.data)->EmployeeId, L"The employee id is not correct for row 1.");
        Assert::AreEqual(e1.DepartmentId, ((LargeRecord*)ptr1.data)->DepartmentId, L"The department id is not correct for row 1.");
        
        Assert::AreNotEqual(RowPtr::EndOfRow, ptr2.length, L"row2 is EndOfRow.");
        Assert::AreEqual(e2.EmployeeId, ((LargeRecord*)ptr2.data)->EmployeeId, L"The employee id is not correct for row 2.");
        Assert::AreEqual(e2.DepartmentId, ((LargeRecord*)ptr2.data)->DepartmentId, L"The department id is not correct for row 2.");

        Assert::AreEqual(RowPtr::EndOfRow, ptr3.length, L"The row count returned is not correct.");
    }
};

